package com.study.bind;

import com.study.enums.SqlType;
import com.study.pojo.Configuration;
import com.study.pojo.MappedStatement;
import com.study.sqlSession.SqlSession;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class MapperMethod {
    private final SqlCommand command;
    private final MethodSignature method;

    public <T> MapperMethod(Class<T> mapperInterface, Method method, Configuration configuration) {
        this.command = new SqlCommand(mapperInterface, method, configuration);
        this.method = new MethodSignature(mapperInterface, method, configuration);
    }

    public Object execute(SqlSession sqlSession, Object[] args) throws Exception {
        Object result = null;
        Object param = (args == null) ? null : args[0];
        switch (command.sqlType) {
            case INSERT: {
                result = sqlSession.insert(command.getName(), param);
                break;
            }
            case UPDATE: {
                result = sqlSession.update(command.getName(), param);
                break;
            }
            case DELETE: {
                result = sqlSession.delete(command.getName(), param);
                break;
            }
            case SELECT: {
                if (method.isReturnsMany()) {
                    result = sqlSession.selectList(command.getName(), param);
                }
                if (method.isReturnsOne()) {
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            }
            default: throw new Exception("Unknown execution method for: " + command.getName());
        }
        return result;
    }

    public static class SqlCommand {
        private final String name;
        private final SqlType sqlType;

        public <T> SqlCommand(Class<T> mapperInterface, Method method, Configuration configuration)  {
            SqlType sqlType1;
            final String className = mapperInterface.getName();
            final String methodName = method.getName();
            String statementId = className + "." + methodName;
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            name = statementId;
            if (mappedStatement == null) {
                sqlType1 = SqlType.UNKNOWN;
            } else {
                try {
                    sqlType1 = SqlType.valueOf(mappedStatement.getSqlType().toUpperCase());
                } catch (IllegalArgumentException e) {
                    sqlType1 = SqlType.UNKNOWN;
                }
            }
            sqlType = sqlType1;
        }

        public String getName() {
            return name;
        }

        public SqlType getSqlType() {
            return sqlType;
        }
    }

    public static class MethodSignature {
        private final boolean returnsMany;
        private final boolean returnsOne;

        public <T> MethodSignature(Class<T> mapperInterface, Method method, Configuration configuration) {
            Class<?> returnType = method.getReturnType();
            Type genericReturnType = method.getGenericReturnType();
            returnsMany = genericReturnType instanceof ParameterizedType;
            returnsOne = !(genericReturnType instanceof ParameterizedType);
        }

        public boolean isReturnsMany() {
            return returnsMany;
        }

        public boolean isReturnsOne() {
            return returnsOne;
        }
    }
}
